/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections.buffer; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.Random; import junit.framework.Test; import junit.framework.TestSuite; import org.apache.commons.collections.Buffer; import org.apache.commons.collections.BufferUnderflowException; import org.apache.commons.collections.ComparatorUtils; import org.apache.commons.collections.collection.AbstractTestCollection; import org.apache.commons.collections.comparators.ComparableComparator; import org.apache.commons.collections.comparators.ReverseComparator; /** * Tests the PriorityBuffer. * * @version $Revision: 646780 $ $Date: 2008-04-10 13:48:07 +0100 (Thu, 10 Apr 2008) $ * * @author Michael A. Smith * @author Steve Phelps */ public class TestPriorityBuffer extends AbstractTestCollection { public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } public static Test suite() { return new TestSuite(TestPriorityBuffer.class); } public TestPriorityBuffer(String testName) { super(testName); } //----------------------------------------------------------------------- public void verify() { super.verify(); PriorityBuffer heap = (PriorityBuffer) collection; Comparator c = heap.comparator; if (c == null) { c = ComparatorUtils.naturalComparator(); } if (!heap.ascendingOrder) { c = ComparatorUtils.reversedComparator(c); } Object[] tree = heap.elements; for (int i = 1; i <= heap.size; i++) { Object parent = tree[i]; if (i * 2 <= heap.size) { assertTrue("Parent is less than or equal to its left child", c.compare(parent, tree[i * 2]) <= 0); } if (i * 2 + 1 < heap.size) { assertTrue("Parent is less than or equal to its right child", c.compare(parent, tree[i * 2 + 1]) <= 0); } } } //----------------------------------------------------------------------- /** * Overridden because BinaryBuffer isn't fail fast. * @return false */ public boolean isFailFastSupported() { return false; } //----------------------------------------------------------------------- public Collection makeConfirmedCollection() { return new ArrayList(); } public Collection makeConfirmedFullCollection() { ArrayList list = new ArrayList(); list.addAll(Arrays.asList(getFullElements())); return list; } /** * Return a new, empty {@link Object} to used for testing. */ public Collection makeCollection() { return new PriorityBuffer(); } //----------------------------------------------------------------------- public Object[] getFullElements() { return getFullNonNullStringElements(); } public Object[] getOtherElements() { return getOtherNonNullStringElements(); } //----------------------------------------------------------------------- public void testBufferEmpty() { resetEmpty(); Buffer buffer = (Buffer) collection; assertEquals(0, buffer.size()); assertEquals(true, buffer.isEmpty()); try { buffer.get(); fail(); } catch (BufferUnderflowException ex) {} try { buffer.remove(); fail(); } catch (BufferUnderflowException ex) {} } public void testBasicOps() { PriorityBuffer heap = new PriorityBuffer(); heap.add("a"); heap.add("c"); heap.add("e"); heap.add("b"); heap.add("d"); heap.add("n"); heap.add("m"); heap.add("l"); heap.add("k"); heap.add("j"); heap.add("i"); heap.add("h"); heap.add("g"); heap.add("f"); assertTrue("heap should not be empty after adds", !heap.isEmpty()); for (int i = 0; i < 14; i++) { assertEquals( "get using default constructor should return minimum value in the binary heap", String.valueOf((char) ('a' + i)), heap.get()); assertEquals( "remove using default constructor should return minimum value in the binary heap", String.valueOf((char) ('a' + i)), heap.remove()); if (i + 1 < 14) { assertTrue("heap should not be empty before all elements are removed", !heap.isEmpty()); } else { assertTrue("heap should be empty after all elements are removed", heap.isEmpty()); } } try { heap.get(); fail("NoSuchElementException should be thrown if get is called after all elements are removed"); } catch (BufferUnderflowException ex) {} try { heap.remove(); fail("NoSuchElementException should be thrown if remove is called after all elements are removed"); } catch (BufferUnderflowException ex) {} } public void testBasicComparatorOps() { PriorityBuffer heap = new PriorityBuffer(new ReverseComparator(new ComparableComparator())); assertTrue("heap should be empty after create", heap.isEmpty()); try { heap.get(); fail("NoSuchElementException should be thrown if get is called before any elements are added"); } catch (BufferUnderflowException ex) {} try { heap.remove(); fail("NoSuchElementException should be thrown if remove is called before any elements are added"); } catch (BufferUnderflowException ex) {} heap.add("a"); heap.add("c"); heap.add("e"); heap.add("b"); heap.add("d"); heap.add("n"); heap.add("m"); heap.add("l"); heap.add("k"); heap.add("j"); heap.add("i"); heap.add("h"); heap.add("g"); heap.add("f"); assertTrue("heap should not be empty after adds", !heap.isEmpty()); for (int i = 0; i < 14; i++) { // note: since we're using a comparator that reverses items, the // "minimum" item is "n", and the "maximum" item is "a". assertEquals( "get using default constructor should return minimum value in the binary heap", String.valueOf((char) ('n' - i)), heap.get()); assertEquals( "remove using default constructor should return minimum value in the binary heap", String.valueOf((char) ('n' - i)), heap.remove()); if (i + 1 < 14) { assertTrue("heap should not be empty before all elements are removed", !heap.isEmpty()); } else { assertTrue("heap should be empty after all elements are removed", heap.isEmpty()); } } try { heap.get(); fail("NoSuchElementException should be thrown if get is called after all elements are removed"); } catch (BufferUnderflowException ex) {} try { heap.remove(); fail("NoSuchElementException should be thrown if remove is called after all elements are removed"); } catch (BufferUnderflowException ex) {} } /** * Illustrates bad internal heap state reported in Bugzilla PR #235818. */ public void testAddRemove() { resetEmpty(); PriorityBuffer heap = (PriorityBuffer) collection; heap.add(new Integer(0)); heap.add(new Integer(2)); heap.add(new Integer(4)); heap.add(new Integer(3)); heap.add(new Integer(8)); heap.add(new Integer(10)); heap.add(new Integer(12)); heap.add(new Integer(3)); confirmed.addAll(heap); // System.out.println(heap); Object obj = new Integer(10); heap.remove(obj); confirmed.remove(obj); // System.out.println(heap); verify(); } /** * Generate heaps staring with Integers from 0 - heapSize - 1. * Then perform random add / remove operations, checking * heap order after modifications. Alternates minHeaps, maxHeaps. * * Based on code provided by Steve Phelps in PR #25818 * */ public void testRandom() { int iterations = 500; int heapSize = 100; int operations = 20; Random randGenerator = new Random(); PriorityBuffer h = null; for(int i=0; i < iterations; i++) { if (i < iterations / 2) { h = new PriorityBuffer(true); } else { h = new PriorityBuffer(false); } for(int r = 0; r < heapSize; r++) { h.add( new Integer( randGenerator.nextInt(heapSize)) ); } for( int r = 0; r < operations; r++ ) { h.remove(new Integer(r)); h.add(new Integer(randGenerator.nextInt(heapSize))); } checkOrder(h); } } /** * Pops all elements from the heap and verifies that the elements come off * in the correct order. NOTE: this method empties the heap. */ protected void checkOrder(PriorityBuffer h) { Integer lastNum = null; Integer num = null; while (!h.isEmpty()) { num = (Integer) h.remove(); if (h.ascendingOrder) { assertTrue(lastNum == null || num.intValue() >= lastNum.intValue()); } else { // max heap assertTrue(lastNum == null || num.intValue() <= lastNum.intValue()); } lastNum = num; num = null; } } /** * Returns a string showing the contents of the heap formatted as a tree. * Makes no attempt at padding levels or handling wrapping. */ protected String showTree(PriorityBuffer h) { int count = 1; StringBuffer buffer = new StringBuffer(); for (int offset = 1; count < h.size() + 1; offset *= 2) { for (int i = offset; i < offset * 2; i++) { if (i < h.elements.length && h.elements[i] != null) buffer.append(h.elements[i] + " "); count++; } buffer.append('\n'); } return buffer.toString(); } /** * Generates 500 randomly initialized heaps of size 100 * and tests that after serializing and restoring them to a byte array * that the following conditions hold: * * - the size of the restored heap is the same * as the size of the orignal heap * * - all elements in the original heap are present in the restored heap * * - the heap order of the restored heap is intact as * verified by checkOrder() */ public void testSerialization() { int iterations = 500; int heapSize = 100; PriorityBuffer h; Random randGenerator = new Random(); for (int i = 0; i < iterations; i++) { if (i < iterations / 2) { h = new PriorityBuffer(true); } else { h = new PriorityBuffer(false); } for (int r = 0; r < heapSize; r++) { h.add(new Integer(randGenerator.nextInt(heapSize))); } assertTrue(h.size() == heapSize); PriorityBuffer h1 = serializeAndRestore(h); assertTrue(h1.size() == heapSize); Iterator hit = h.iterator(); while (hit.hasNext()) { Integer n = (Integer) hit.next(); assertTrue(h1.contains(n)); } checkOrder(h1); } } public PriorityBuffer serializeAndRestore(PriorityBuffer h) { PriorityBuffer h1 = null; try { byte[] objekt = writeExternalFormToBytes(h); h1 = (PriorityBuffer) readExternalFormFromBytes(objekt); } catch (IOException e) { e.printStackTrace(); fail(e.toString()); } catch (ClassNotFoundException e) { e.printStackTrace(); fail(e.toString()); } return h1; } public String getCompatibilityVersion() { return "3.2"; } // public void testCreate() throws Exception { // resetEmpty(); // writeExternalFormToDisk((java.io.Serializable) collection, "C:/commons/collections/data/test/PriorityBuffer.emptyCollection.version3.2.obj"); // resetFull(); // writeExternalFormToDisk((java.io.Serializable) collection, "C:/commons/collections/data/test/PriorityBuffer.fullCollection.version3.2.obj"); // } }